using System;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Camera2DModule {

	public class Game1 : Game {

		private const int ViewportWidth = 600;
		private const int ViewportHeight = 400;
		private const int WorldWidth = 1200;
		private const int WorldHeight = 800;
		private const float ZoomIncrement = 0.1f;
        private Rectangle WorldBounds;
		private readonly GraphicsDeviceManager Graphics;
		private Texture2D BlankTexture;
		private Camera Camera2D;
		private MouseState LastMouseState;
	    private KeyboardState LastKeyboardState;
		private SpriteBatch SpriteBatch;
		private Matrix Transformation;

        //*****
        // Player
	    private Texture2D playerTexture;
	    private int playerBoundsWidth = 64;
	    private int playerBoundsHeight = 64;
	    private Vector2 playerPosition = new Vector2(ViewportWidth*0.5f - 32, ViewportHeight*0.5f - 32);
	    private float playerVelocity = 5f;
	    private Rectangle playerBounds;

        private Texture2D PineappleTexture;
        private int pineappleBoundsWidth = 64;
        private int pineappleBoundsHeight = 64;

        private Vector2 NearPineapplePosition = new Vector2(0, 200);
        private Vector2 FarPineapplePosition = new Vector2(0, 200);
        private Vector2 FarthestPineapplePosition = new Vector2(0, 200);

        private float NearPineappleVelocity = 2f;
        private float FarPineappleVelocity = 2f;
        private float FarthestPineappleVelocity = 2f;

        private float NearPineappleDepth = 1f;
        private float FarPineappleDepth = 2f;
        private float FarthestPineappleDepth = 4f;

        private Rectangle NearPineappleBounds;
        private Rectangle FarPineappleBounds;
        private Rectangle FarthestPineappleBounds;

	    private Texture2D brickTexture;
	    private int brickTextureWidth = 128;
	    private int brickTextureHeight = 128;
		//*****

        static Game1() {
        }

		public Game1() {
			Graphics = new GraphicsDeviceManager(this);
			Content.RootDirectory = "Content";
			Graphics.PreferredBackBufferWidth = 600;
			Graphics.PreferredBackBufferHeight = 400;
			IsMouseVisible = true;
		}

		protected override void LoadContent() {
			SpriteBatch = new SpriteBatch(GraphicsDevice);
			BlankTexture = Content.Load<Texture2D>("Blank");
			Camera2D = new Camera(Graphics.GraphicsDevice.Viewport, WorldWidth, WorldHeight, 1f) {
				Position = new Vector2(ViewportWidth * 0.5f, ViewportHeight * 0.5f) // Start off in the center of the viewport.
			};

            //*****
            // World
            WorldBounds = new Rectangle(0, 0, WorldWidth, WorldHeight);
            // Player
            playerTexture = Content.Load<Texture2D>("player");
            playerBounds = new Rectangle(playerBounds.X,
                                         playerBounds.Y,
                                         playerBoundsWidth,
                                         playerBoundsHeight);
            // Entities
            PineappleTexture = Content.Load<Texture2D>("pineapple");
            NearPineappleBounds = new Rectangle((int)NearPineapplePosition.X,
                                                (int)NearPineapplePosition.Y,
                                                (int)(pineappleBoundsWidth * (1 / NearPineappleDepth)),
                                                (int)(pineappleBoundsHeight * (1 / NearPineappleDepth)));
            FarPineappleBounds = new Rectangle((int)FarPineapplePosition.X,
                                               (int)FarPineapplePosition.Y,
                                               (int)(pineappleBoundsWidth * (1 / FarPineappleDepth)),
                                               (int)(pineappleBoundsHeight * (1 / FarPineappleDepth)));
            FarthestPineappleBounds = new Rectangle((int)FarthestPineapplePosition.X, 
                                                    (int)FarthestPineapplePosition.Y, 
                                                    (int)(pineappleBoundsWidth * (1/FarthestPineappleDepth)),
                                                    (int)(pineappleBoundsHeight * (1/FarthestPineappleDepth)));

            // Background
		    brickTexture = Content.Load<Texture2D>("brick");
		    //*****
		}

		protected override void Update(GameTime gameTime) {

			KeyboardState keyboardState = Keyboard.GetState();

            if (Camera2D.IsFollowingPlayer) {
                Vector2 movement = Vector2.Zero;

		        if (keyboardState.IsKeyDown(Keys.Left)) {
		            movement.X--;
		        }
		        if (keyboardState.IsKeyDown(Keys.Right)) {
		            movement.X++;
		        }
		        if (keyboardState.IsKeyDown(Keys.Up)) {
		            movement.Y--;
		        }
		        if (keyboardState.IsKeyDown(Keys.Down)) {
		            movement.Y++;
                }

                playerPosition += movement * playerVelocity;
                // Reset bounds by location, which is the foundation for all.
                playerBounds.X = (int)playerPosition.X;
                playerBounds.Y = (int)playerPosition.Y;
                Camera2D.Position = playerPosition; // Camera follows player.
                //Camera2D.Move(movement);
		    }

		    if (LastKeyboardState.IsKeyUp(Keys.E) && keyboardState.IsKeyDown(Keys.E)) {
                Camera2D.Position = new Vector2(900, 200); // Center of top-right panel.
                Camera2D.IsFollowingPlayer = false;
            } else if (LastKeyboardState.IsKeyUp(Keys.R) && keyboardState.IsKeyDown(Keys.R)) {
                Camera2D.IsFollowingPlayer = true;
            }

		    LastKeyboardState = keyboardState;

			//----------

			MouseState mouseState = Mouse.GetState();
			if(mouseState.ScrollWheelValue > LastMouseState.ScrollWheelValue) {
				Camera2D.Zoom += ZoomIncrement;
			} else if(mouseState.ScrollWheelValue < LastMouseState.ScrollWheelValue) {
				Camera2D.Zoom -= ZoomIncrement;
			}

			Vector2 viewportMousePosition = new Vector2(mouseState.X, mouseState.Y);

			//----------

			Transformation = Camera2D.GetTransformation();

			//----------

			// Transform mouse input from view to world position
			Vector2 worldMousePosition = Vector2.Transform(new Vector2(mouseState.X, mouseState.Y), Matrix.Invert(Transformation));

			if(LastMouseState.LeftButton != ButtonState.Pressed && mouseState.LeftButton == ButtonState.Pressed) {
				Debug.WriteLine(viewportMousePosition + ", " + worldMousePosition);
            }

            LastMouseState = mouseState;

			//*****
            
            // TODO: These location updates need to be in pixels/sec. Right now locked at 60 * 2;

            /*
             * Player.
             * Player is directly controlled, so no velocity flipping.
             */

            if (playerBounds.Left < WorldBounds.Left) {  // Test Bounds.
                playerPosition.X = WorldBounds.Left;     // Set Location.
            }
            else if (playerBounds.Right > WorldBounds.Right) {
                playerPosition.X = WorldBounds.Right - playerBounds.Width; // TODO: is this absolutely correct?
            }
            // Reset bounds by location, which is the foundation for all.
            playerBounds.X = (int)playerPosition.X;
            playerBounds.Y = (int)playerPosition.Y;

            /*
             * Widgets.
             */

            if (NearPineappleBounds.Left < WorldBounds.Left) {  // Test Bounds.
                NearPineapplePosition.X = WorldBounds.Left;     // Set Location.
                NearPineappleVelocity = -NearPineappleVelocity; // Flip Velocity.
            } else if (NearPineappleBounds.Right > WorldBounds.Right) {
                NearPineapplePosition.X = WorldBounds.Right-NearPineappleBounds.Width; // TODO: is this absolutely correct?
                NearPineappleVelocity = -NearPineappleVelocity;
            }
            else {
                NearPineapplePosition.X += (NearPineappleVelocity / NearPineappleDepth);
            }
            // Reset bounds by location, which is the foundation for all.
            NearPineappleBounds.X = (int)NearPineapplePosition.X;
            NearPineappleBounds.Y = (int)NearPineapplePosition.Y;

			if(FarPineappleBounds.Left < WorldBounds.Left) {  // Test Bounds.
				FarPineapplePosition.X = WorldBounds.Left;     // Set Location.
				FarPineappleVelocity = -FarPineappleVelocity; // Flip Velocity.
			} else if(FarPineappleBounds.Right > WorldBounds.Right) {
				FarPineapplePosition.X = WorldBounds.Right-FarPineappleBounds.Width; // TODO: is this absolutely correct?
				FarPineappleVelocity = -FarPineappleVelocity;
			} else {
				FarPineapplePosition.X += (FarPineappleVelocity / FarPineappleDepth);
			}
			// Reset bounds by location, which is the foundation for all.
			FarPineappleBounds.X = (int)FarPineapplePosition.X;
			FarPineappleBounds.Y = (int)FarPineapplePosition.Y;

			if(FarthestPineappleBounds.Left < WorldBounds.Left) {  // Test Bounds.
				FarthestPineapplePosition.X = WorldBounds.Left;     // Set Location.
				FarthestPineappleVelocity = -FarthestPineappleVelocity; // Flip Velocity.
			} else if(FarthestPineappleBounds.Right > WorldBounds.Right) {
				FarthestPineapplePosition.X = WorldBounds.Right-FarthestPineappleBounds.Width; // TODO: is this absolutely correct?
				FarthestPineappleVelocity = -FarthestPineappleVelocity;
			} else {
				FarthestPineapplePosition.X += (FarthestPineappleVelocity / FarthestPineappleDepth);
			}
			// Reset bounds by location, which is the foundation for all.
			FarthestPineappleBounds.X = (int)FarthestPineapplePosition.X;
			FarthestPineappleBounds.Y = (int)FarthestPineapplePosition.Y;
			//*****

			base.Update(gameTime);
		}

		protected override void Draw(GameTime gameTime) {
			GraphicsDevice.Clear(Color.Black);

			// Transformation operates on everything drawn with this 'Begin/End' sequence.
			SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, Transformation);

            // Bricks
            for (int i = 0; i < 10; i++) {
                for (int j = 0; j < 2; j++) {
                    SpriteBatch.Draw(brickTexture, new Vector2(i*brickTextureWidth, j*brickTextureHeight), Color.White);
                }
            }

                // Corner markers
                SpriteBatch.Draw(BlankTexture, new Rectangle(10, 10, 10, 10), Color.LightBlue);
			SpriteBatch.Draw(BlankTexture, new Rectangle(10, 400 - 30, 10, 10), Color.DarkBlue);
			SpriteBatch.Draw(BlankTexture, new Rectangle(600 - 30, 10, 10, 10), Color.LightGreen);
			SpriteBatch.Draw(BlankTexture, new Rectangle(600 - 30, 400 - 30, 10, 10), Color.DarkGreen);
			SpriteBatch.Draw(BlankTexture, new Rectangle(600 + 30, 10, 10, 10), Color.LightGreen);
			SpriteBatch.Draw(BlankTexture, new Rectangle(600 + 30, 400 - 30, 10, 10), Color.DarkGreen);
			SpriteBatch.Draw(BlankTexture, new Rectangle(1200 - 30, 10, 10, 10), Color.Pink);
			SpriteBatch.Draw(BlankTexture, new Rectangle(1200 - 30, 400 - 30, 10, 10), Color.DarkRed);
			SpriteBatch.Draw(BlankTexture, new Rectangle(10, 800 - 30, 10, 10), Color.DarkBlue);
			SpriteBatch.Draw(BlankTexture, new Rectangle(600 - 30, 800 - 30, 10, 10), Color.DarkGreen);
			SpriteBatch.Draw(BlankTexture, new Rectangle(600 + 30, 800 - 30, 10, 10), Color.DarkGreen);
			SpriteBatch.Draw(BlankTexture, new Rectangle(1200 - 30, 800 - 30, 10, 10), Color.DarkRed);

			//*****
            // Player
            SpriteBatch.Draw(playerTexture, playerPosition, Color.White);
            // Entities
			SpriteBatch.Draw(PineappleTexture, NearPineapplePosition, Color.White);
			SpriteBatch.Draw(PineappleTexture, FarPineapplePosition, null, Color.White, 0, Vector2.Zero, new Vector2(1/FarPineappleDepth), SpriteEffects.None, 0);
			SpriteBatch.Draw(PineappleTexture, FarthestPineapplePosition, null, Color.White, 0, Vector2.Zero, new Vector2(1/FarthestPineappleDepth), SpriteEffects.None, 0);
			//*****

			SpriteBatch.End();

			base.Draw(gameTime);
		}

		// TODO: Bounding box, picking.

	}

}